home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 424_01 / ed_157 / edit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-27  |  42.2 KB  |  1,719 lines

  1. /*
  2.  * Copyright (C) 1992 by Rush Record
  3.  * Copyright (C) 1993 by Charles Sandmann (sandmann@clio.rice.edu)
  4.  * 
  5.  * This file is part of ED.
  6.  * 
  7.  * ED is free software; you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation.
  9.  * 
  10.  * ED is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  11.  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12.  * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  13.  * 
  14.  * You should have received a copy of the GNU General Public License along with ED
  15.  * (see the file COPYING).  If not, write to the Free Software Foundation, 675
  16.  * Mass Ave, Cambridge, MA 02139, USA.
  17.  */
  18. #include "opsys.h"
  19.  
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23.  
  24. #include "memory.h"
  25. #include "ctyp_dec.h"
  26. #include "rec.h"
  27. #include "window.h"
  28. #include "ed_dec.h"    /* global data */
  29. #include "buffer.h"
  30. #include "buf_dec.h"    /* kill buffers */
  31. #include "cmd_enum.h"
  32. #include "handy.h"    /* for definition of max() */
  33. #include "keyvals.h"    /* for numeric values corresponding to editor commands */
  34. #include "file.h"
  35.  
  36. static buf_node charbuf = {1,1,0,0};    /* single-char buffer for insertion of single chars */
  37. static rec_node charrec = {0,0,1,0};
  38. static Char character;
  39. static buf_node multibuf = {1,1,0,0};    /* buffer for insertion of repeated chars */
  40. static rec_node multirec = {0,0,0,0};
  41. static buf_node pagebuf = {0,1,0,0};    /* buffer to hold page delimiter string during page moves */
  42. static Int specify_position = 0;    /* flags a user- or bookmark-specified position in file (-innn) */
  43. static Int specify_byt = 0;    /* flags a bookmark-specified byte offset in record */
  44. static Int dirpos[128];    /* record numbers in directory listings */
  45. static Char *greptitle[128];    /* titles of grep windows (used when popping back into them) */
  46. static rec_ptr grepdata[128];    /* contents of grep windows */
  47.  
  48. extern rec_ptr dir_find();
  49. #ifndef NO_FTP
  50. extern Char *host_filename();
  51. #endif
  52.  
  53. /******************************************************************************\
  54. |Routine: dir_depth
  55. |Callby: edit
  56. |Purpose: Calculates the depth of a directory on a filesystem.
  57. |Arguments:
  58. |    dir is the directory specification.
  59. \******************************************************************************/
  60. Int dir_depth(dir)
  61. Char *dir;
  62. {
  63.     Char *p,c;
  64.     Int curdirpos,ftpsystem;
  65.  
  66.     ftpsystem = ftp_system(dir);
  67.     if(ftpsystem == 'V' || ftpsystem == 'I')
  68.     {    
  69.         for(p = dir + 1;(c = *p++);)
  70.             if(c == '[' || c == '<')
  71.                 break;
  72.         if(!strncmp(p,"000000]",strlen("000000]")))
  73.             curdirpos = 0;
  74.         else
  75.         {
  76.             for(curdirpos = 1;(c = *p++);)
  77.                 if(c == '.')
  78.                     curdirpos++;
  79.                 else if(c == ']' || c == '>')
  80.                     break;
  81.         }
  82.     }
  83.     else
  84.     {
  85.         for(p = dir + 1,curdirpos = 0;(c = *p++);curdirpos += (c == '/'));    /* count the '/' characters (past the first one) */
  86.     }
  87.     return(curdirpos);
  88. }
  89.  
  90. /******************************************************************************\
  91. |Routine: position_buffer
  92. |Callby: edit
  93. |Purpose: Sets up a buffer to put the cursor at a specified position.
  94. |Arguments:
  95. |    rec is the line number to start the cursor out on. Line 1 is the first line
  96. |        in the file.
  97. |    byt is the byte offset in that record.
  98. \******************************************************************************/
  99. void position_buffer(rec,byt)
  100. Int rec,byt;
  101. {
  102.     Int i;
  103.     
  104.     if((CURREC = BASE->next) != BASE)    /* if it isn't an empty buffer */
  105.     {
  106.         while(--rec)    /* advance in buffer as directed */
  107.         {
  108.             if(CURREC == BASE)
  109.                 break;
  110.             CURREC = CURREC->next;
  111.         }
  112.         for(TOPREC = CURREC,i = TOPLIM - TOPROW;i--;TOPREC = TOPREC->prev)    /* try to back up to leave margin at top */
  113.             if(TOPREC == BASE->next)
  114.                 break;
  115.         for(i = BOTROW - TOPROW,BOTREC = TOPREC;i--;BOTREC = BOTREC->next)    /* establish BOTREC */
  116.             if(BOTREC == BASE)
  117.                 break;
  118.         while(i-- >= 0)    /* deal with being too close to the <eob> */
  119.         {
  120.             if(TOPREC == BASE->next)
  121.                 break;
  122.             TOPREC = TOPREC->prev;
  123.         }
  124.         for(BOTREC = TOPREC;BOTREC != CURREC;BOTREC = BOTREC->next,CURROW++);
  125.         BOTREC = TOPREC;
  126.         CURBYT = byt;
  127.         if(CURBYT > CURREC->length)
  128.             CURBYT = CURREC->length;
  129.         CURCOL = get_column(CURREC,CURBYT);
  130.     }
  131.     else    /* empty buffer */
  132.         TOPREC = BOTREC = CURREC = BASE;
  133. }
  134.  
  135. /******************************************************************************\
  136. |Routine: edit_init_pos
  137. |Callby: main parse_fnm
  138. |Purpose: Sets the initial position in a file.
  139. |Arguments:
  140. |    pos is the line number to start the cursor out on. Line 1 is the first line in the file.
  141. \******************************************************************************/
  142. void edit_init_pos(pos)
  143. Int pos;
  144. {
  145.     specify_position = pos;
  146. }
  147.  
  148. /******************************************************************************\
  149. |Routine: edit_init_byt
  150. |Callby: main
  151. |Purpose: Sets the byte offset when initial positioning occurs through a
  152. |         bookmark.
  153. |Arguments:
  154. |    pos is the byte offset to start out with.
  155. \******************************************************************************/
  156. void edit_init_byt(pos)
  157. Int pos;
  158. {
  159.     specify_byt = pos;
  160. }
  161.  
  162. /******************************************************************************\
  163. |Routine: edit
  164. |Callby: main
  165. |Purpose: Implements the user interface.
  166. |Arguments:
  167. |    fname is the name of the file being edited.
  168. |    multiplefiles is a flag that -m appeared on the command line.
  169. |    comm is the remainder of the command line.
  170. |    commlen is the length of the command buffer.
  171. \******************************************************************************/
  172. void edit(fname,multiplefiles,comm,commlen)
  173. Char *fname;    /* the name of the file we're editing */
  174. Char *multiplefiles;    /* flags -m on command line */
  175. Char *comm;    /* buffer containing remainder of command line (examined if -m specified) */
  176. Int commlen;    /* length of command buffer */
  177. {
  178. /* this table is a list of flags that tell whether a given key is allowed
  179.    when pressed in a diredit buffer. the 128th entry corresponds to NULL,
  180.    with entries going negative and positive from there.
  181. */
  182.     static Schar diredit_keymodes[256] =
  183.     {
  184. /*      . a b c d e f g h i j k l m n o p q r s t u v w x y z [ \ ] ^ _ */
  185.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  186.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  187.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,0,0,1,0,1,1,
  188.         1,1,0,1,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
  189.         1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,
  190.         0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* the zero is SPACE */
  191.         1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* the zeroes are A and B */
  192.         1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* the zeroes are a and b */
  193.     };
  194.     static Schar *diredit_keymode = diredit_keymodes + 128;
  195.     static Schar group_keymodes[256] =
  196.     {
  197. /*      . a b c d e f g h i j k l m n o p q r s t u v w x y z [ \ ] ^ _ */
  198.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  199.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  200.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,0,0,1,0,1,1,
  201.         1,1,0,1,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
  202.         1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,
  203.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  204.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,    /* the zeroes are R and V */
  205.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,    /* the zeroes are r and v */
  206.     };
  207.     static Schar *group_keymode = group_keymodes + 128;
  208.     static Schar article_keymodes[256] =
  209.     {
  210. /*      . a b c d e f g h i j k l m n o p q r s t u v w x y z [ \ ] ^ _ */
  211.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  212.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  213.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,0,0,1,0,1,1,
  214.         1,1,0,1,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
  215.         1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,
  216.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  217.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,    /* the zeroes are P, R and V */
  218.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,    /* the zeroes are p, r and v */
  219.     };
  220.     static Schar *article_keymode = article_keymodes + 128;
  221.     
  222.     Int i,j,k,n,deliberate,was_displaying,artnum,read,close,bodycount;
  223.     Int byte,offset,record,searchlength,repeat;
  224.     Int laststatcol,newstatcol,excess,total,bookpos,bookbyt,curdirpos,upmove,binary;
  225.     Char buf[512],editfile[512],recfile[512],bookmark[512],ftpfile[512],artcom[512];
  226.     Char recover,process,*p,bdummy,*filebuf,diredit;
  227.     Schar term,keybuf;
  228.     Uchar *casetable;
  229.     rec_ptr base,rec,rec1,rec2,new,save_rec;
  230.     Char *q;
  231.  
  232.     laststatcol = NCOL;
  233.     charbuf.first = &charrec;    /* initialize single char buffer */
  234.     charbuf.last = &charrec;
  235.     charrec.next = (rec_ptr)&charbuf;
  236.     charrec.prev = (rec_ptr)&charbuf;
  237.     charrec.data = &character;
  238.  
  239.     multibuf.first = &multirec;    /* initialize repeated-char buffer */
  240.     multibuf.last = &multirec;
  241.     multirec.next = (rec_ptr)&multibuf;
  242.     multirec.prev = (rec_ptr)&multibuf;
  243.  
  244.     pagebuf.first = (rec_ptr)&pagebuf.first;    /* initialize page delimiter buffer */
  245.     pagebuf.last = (rec_ptr)&pagebuf.first;
  246.  
  247.     TOPROW = 2;    /* set up the screen-oriented context */
  248.     BOTROW = NROW;
  249.     calc_limits(TOPROW,BOTROW,&TOPLIM,&BOTLIM);
  250.     CURROW = 2;
  251.     CURCOL = 1;
  252.     FIRSTCOL = 1;
  253.     WANTCOL = 1;
  254.     NWINDOWS = 0;
  255.     CURWINDOW = new_window(fname);    /* this increments NWINDOWS */
  256.     DIRECTION = 1;
  257.     SELREC = 0;
  258.     deliberate = 0;    /* no deliberate sideways scrolling in effect */
  259.     memset(dirpos,0,sizeof(dirpos));    /* dirpos[i] = 0 means we haven't visited it yet */
  260.     HEXMODE = (WINDOW[CURWINDOW].binary == 2);
  261.  
  262.     CURBYT = 0;    /* initialize the first buffer */
  263.     if(specify_position)    /* deal with -i option if present */
  264.     {
  265.         position_buffer(specify_position,specify_byt);
  266.         specify_position = 0;    /* it's a one-shot */
  267.         specify_byt = 0;
  268.     }
  269.     else    /* position not specified */
  270.         TOPREC = BOTREC = CURREC = BASE->next;
  271.     for(i = 2;i < NROW;i++)    /* establish BOTREC */
  272.     {
  273.         if(BOTREC == BASE)
  274.             break;
  275.         BOTREC = BOTREC->next;
  276.     }
  277.     if(i < NROW)
  278.         BOTREC = 0;
  279.  
  280.     ref_display();    /* display the file */
  281.     marge(TOPROW,BOTROW);    /* set up scrolling region */
  282.     save_window();    /* load WINDOW[0] in case it's needed */
  283. /* handle -m on command line */
  284.     if(*multiplefiles)
  285.     {
  286.         while(1)
  287.         {
  288.             save_window();
  289.             if(NWINDOWS >= MAX_WINDOWS)
  290.                 break;
  291.             for(total = i = 0;i < NWINDOWS;i++)
  292.                 if((excess = WINDOW[i].botrow - WINDOW[i].toprow - 2) > 0)    /* there is space available here */
  293.                     total += excess;
  294.             if(total < 4)
  295.                 break;
  296.             recover = process = 0;
  297.             parse_skip(1);    /* flag no prompting in parse_fnm */
  298.             if(!parse_filename(comm,commlen,editfile,&recover,recfile,&process,multiplefiles,&bdummy,&bdummy))
  299.                 break;
  300.             base = (rec_ptr)imalloc(sizeof(rec_node));
  301.             base->length = 0;
  302.             base->data = NULL;
  303.             if(load_file(editfile,base,bookmark,&bookpos,&bookbyt,&diredit,binary = WINDOW[CURWINDOW].binary,&filebuf) != -2)
  304.             {
  305.                 insert_window(editfile,base,binary,(int)diredit);    /* note, this routine may call set_window in order to scroll reduced windows */
  306.                 if((i = strlen(bookmark)))
  307.                 {
  308.                     WINDOW[CURWINDOW].bookmark = (Char *)imalloc(++i);
  309.                     strcpy(WINDOW[CURWINDOW].bookmark,bookmark);
  310.                     if(--bookpos > 0)
  311.                         down_arrow(bookpos);
  312.                     if(bookbyt)
  313.                         right_arrow(bookbyt);
  314.                 }
  315.                 WINDOW[CURWINDOW].filebuf = filebuf;
  316.                 WINDOW[CURWINDOW].binary = binary;
  317.                 WINDOW[CURWINDOW].news = 0;
  318.             }
  319.         }
  320.     }
  321.     while(1)    /* main loop */
  322.     {
  323.         if(!deliberate)    /* deliberate != 0 means they shifted left or right deliberately */
  324.         {
  325.             if((i = CURCOL - NCOL - FIRSTCOL) > -2)
  326.             {
  327.                 FIRSTCOL += i + 10;
  328.                 ref_window(CURWINDOW);
  329.             }
  330.             else if(CURCOL < FIRSTCOL)
  331.             {
  332.                 FIRSTCOL = max(1,CURCOL - 10);
  333.                 ref_window(CURWINDOW);
  334.             }
  335.         }
  336.         else
  337.             deliberate = 0;
  338.         if(REPORTSTATUS && puttest())    /* handle cursor position report if necessary */
  339.         {
  340.             get_position(&record,&byte);
  341.             sprintf(buf,"Rec %d,Byt %d,Col %d",record,CURBYT + 1,CURCOL - FIRSTCOL + 1);
  342.             if(OVERSTRIKE)
  343.                 strcat(buf,",Ovr");
  344.             if(BOXCUT)
  345.                 strcat(buf,",Box");
  346.             newstatcol = NCOL - strlen(buf) + FIRSTCOL;
  347.             if(laststatcol < newstatcol)
  348.             {
  349.                 move(TOPROW - 1,laststatcol);
  350.                 ers_end();
  351.             }
  352.             move(TOPROW - 1,laststatcol = newstatcol);
  353.             reverse();
  354.             putz(buf);
  355.             normal();
  356.         }
  357.         if(WINDOW[CURWINDOW].diredit)
  358.             if(CURREC != BASE)
  359.                 if(CURCOL < 23)
  360.                 {
  361.                     CURCOL = 23;
  362.                     CURBYT = 22;
  363.                 }
  364.         move(CURROW,CURCOL);    /* move to where we are */
  365.         repeat = get_next_key(&keybuf);
  366.         unmark_paren();    /* remove parenthesis marking, if any */
  367. /* verify the keystroke is allowed for a diredit window */
  368.         if((WINDOW[CURWINDOW].diredit && diredit_keymode[keybuf]))
  369.         {
  370.             bell();
  371.             continue;
  372.         }
  373. /* verify the keystroke is allowed for a group window */
  374.         if(WINDOW[CURWINDOW].news == 1 && group_keymode[keybuf])
  375.         {
  376.             bell();
  377.             continue;
  378.         }
  379. /* verify the keystroke is allowed for an article window */
  380.         if(WINDOW[CURWINDOW].news == 2 && article_keymode[keybuf])
  381.         {
  382.             bell();
  383.             continue;
  384.         }
  385.         switch(keybuf)
  386.         {
  387. /*******************************************************************************/
  388. /*                              control characters                             */
  389. /*******************************************************************************/
  390.  
  391. /*case KEY_CTRLC:*/             /* control-c, just redisplay */
  392. /*    emulate_key((Schar)KEY_REFRESH,1);
  393.     break;*/
  394.  
  395. case KEY_BACKSPACE:             /* backspace (move to beginning of line, or previous if at beginning) */
  396.     backspace(repeat);
  397.     break;
  398.  
  399. case KEY_DELPREVWORD:             /* delete previous word */
  400.     if(!find_word(-repeat))
  401.     {
  402.         abort_key();
  403.         break;
  404.     }
  405.     if(OVERSTRIKE)
  406.     {
  407.         offset = find_word(-repeat);
  408.         killer(offset,&WORDBUF,0);
  409.     }
  410.     else
  411.     {
  412.         if(repeat > 1)
  413.         {
  414.             offset = find_word(-(repeat - 1));
  415.             killer(offset,0,0);
  416.             WORDBUF.direction = 1;
  417.         }
  418.         offset = find_word(-1);
  419.         killer(offset,&WORDBUF,0);
  420.     }
  421.     WORDBUF.direction = 1;
  422.     WINDOW[CURWINDOW].modified = 1;
  423.     break;
  424.  
  425. case KEY_DEFINEKEY:             /* define key */
  426.     define_key();
  427.     break;
  428.  
  429. case KEY_CARRIAGERETURN:             /* carriage return */
  430. selective:
  431. #ifndef NO_NEWS
  432.     if(WINDOW[CURWINDOW].news == 1)    /* a group selection */
  433.     {
  434.         if(CURREC == BASE)
  435.             bell();
  436.         else
  437.         {
  438.             for(i = 0;i < NWINDOWS;i++)    /* find out if an article is displayed */
  439.                 if(WINDOW[i].news == 3)
  440.                     break;
  441.             if(i < NWINDOWS)    /* destroy the article window */
  442.             {
  443.                 save_window();
  444.                 remove_window(i);
  445.                 for(j = 0;j < NMARK;j++)    /* adjust marks */
  446.                 {
  447.                     if(MARKWINDOW[j] == i)
  448.                     {
  449.                         for(k = j + 1;k < NMARK;k++)
  450.                         {
  451.                             MARKWINDOW[k - 1] = MARKWINDOW[k];
  452.                             MARKREC[k - 1] = MARKREC[k];
  453.                             MARKBYT[k - 1] = MARKBYT[k];
  454.                         }
  455.                         NMARK--;
  456.                         j--;
  457.                     }
  458.                 }
  459.             }
  460.             for(i = 0;i < NWINDOWS;i++)
  461.                 if(WINDOW[i].news == 2)
  462.                     break;
  463.             newsrc_current(CURREC->data + groupname_offset());    /* figures out which entry in NETRC corresponds to current group, save internal pointer */
  464.             news_group(CURREC->data + groupname_offset(),i,keybuf);
  465.         }
  466.     }
  467.     else if(WINDOW[CURWINDOW].news == 2)    /* an article selection */
  468.     {
  469.         if(keybuf == 'P')    /* there is no distinction between p and P */
  470.             keybuf = tolower(keybuf);
  471.         if(keybuf == 'p' && !posting_allowed())
  472.         {
  473.             bell();
  474.             slip_message("Posting is not allowed by this news server.");
  475.             wait_message();
  476.             break;
  477.         }
  478. /* if retrieving an article, don't allow them off the end of the list */
  479.         if(CURREC == BASE && keybuf != 'p')
  480.             bell();
  481.         else
  482.         {
  483. /* retrieve the article */
  484.             if(CURREC != BASE)
  485.             {
  486.                 my_sscanf(CURREC->data,"%d",&artnum);    /* get article number */
  487.                 sprintf(artcom,"STAT %d",artnum);
  488.                 news_command(artcom);
  489.                 news_response(buf,sizeof(buf));
  490.                 if(buf[0] != '2')
  491.                 {
  492.                     slip_message("An error occurred while STATing that article:");
  493.                     slip_message(buf);
  494.                     wait_message();
  495.                     break;
  496.                 }
  497.             }
  498. /* generate a new buffer */
  499.             base = (rec_ptr)imalloc(sizeof(rec_node));
  500.             base->length = 0;
  501.             base->data = NULL;
  502.             base->next = base->prev = base;
  503. /* retrieve the article as appropriate */
  504.             if(keybuf == 'R' || keybuf == 'V')
  505.                 news_command("ARTICLE");
  506.             else if(keybuf == 'p')
  507.             {
  508.                 if(CURREC == BASE)
  509.                 {
  510.                     if(!inquire("Subject",buf,sizeof(buf),1))
  511.                     {
  512.                         toss_data(base);
  513.                         break;
  514.                     }
  515.                     load_post(base,buf);
  516.                 }
  517.                 else
  518.                 {
  519.                     p = CURREC->data + 42;
  520.                     while(*p == '=')
  521.                         p++;
  522.                     load_post(base,p);
  523.                     news_command(artcom);
  524.                     news_response(buf,sizeof(buf));
  525.                     if(buf[0] != '2')
  526.                     {
  527.                         toss_data(base);
  528.                         slip_message("An error occurred while STATing that article:");
  529.                         slip_message(buf);
  530.                         wait_message();
  531.                         break;
  532.                     }
  533.                     news_command("BODY");
  534.                 }
  535.             }
  536.             else
  537.                 news_command("BODY");
  538.             if(CURREC != BASE)
  539.             {
  540.                 news_response(buf,sizeof(buf));
  541.                 if(buf[0] != '2')
  542.                 {
  543.                     slip_message("An error occurred while retrieving that article:");
  544.                     slip_message(buf);
  545.                     wait_message();
  546.                     toss_data(base);
  547.                     break;
  548.                 }
  549.             }
  550.             if(keybuf != 'p')    /* posting doesn't change article-read state */
  551.             {
  552.                 read = CURREC->recflags & 2;    /* save current state of this entry */
  553.                 CURREC->recflags ^= 2;    /* toggle its state */
  554.                 paint(CURROW,CURROW,FIRSTCOL);
  555.                 if(read)    /* was already marked read, mark it unread */
  556.                     newsrc_mark_unread(artnum);
  557.                 else    /* was not marked as read, mark it read */
  558.                     newsrc_mark_read(artnum);
  559.             }
  560. /* read the article body */
  561.             if(CURREC != BASE)
  562.                 while(1)
  563.                 {
  564.                     news_response(buf,sizeof(buf));
  565.                     if((i = strlen(buf)) == 1 && buf[0] == '.')
  566.                         break;
  567.                     new = (rec_ptr)imalloc(sizeof(rec_node));
  568.                     insq(new,base->prev);
  569. /* if posting buffer, precede each line with "> " */
  570.                     if(keybuf == 'p')
  571.                     {
  572.                         new->data = (Char *)imalloc(i + 3);
  573.                         strcpy(new->data,"> ");
  574.                         strcat(new->data,buf);
  575.                         new->length = i + 2;
  576.                     }
  577.                     else
  578.                     {
  579.                         new->data = (Char *)imalloc(i + 1);
  580.                         strcpy(new->data,buf);
  581.                         new->length = i;
  582.                     }
  583.                     new->recflags = 1;    /* this is a freeable buffer */
  584.                 }
  585.             if(keybuf == 'p')
  586.             {
  587.                 new = (rec_ptr)imalloc(sizeof(rec_node));
  588.                 new->data = (Char *)imalloc(1);
  589.                 *new->data = '\0';
  590.                 new->length = 0;
  591.                 new->recflags = 1;
  592.                 insq(new,base->prev);
  593.                 if(CURREC == BASE)
  594.                     strcpy(artcom,"New Posting");
  595.                 else
  596.                     sprintf(artcom,"Followup to article %d",artnum);
  597.             }
  598.             else
  599.             {
  600.                 down_arrow(1);
  601.                 sprintf(artcom,"Article %d",artnum);
  602.             }
  603. /* if no news=3 buffer exists, create one */
  604.             for(n = 0;n < NWINDOWS;n++)
  605.                 if(WINDOW[n].news == 3)
  606.                     break;
  607.             if(n == NWINDOWS)
  608.             {
  609.                 save_window();
  610.                 insert_window_percent(80);
  611.                 insert_window(artcom,base,0,0);
  612.                 WINDOW[CURWINDOW].filebuf = NULL;
  613.                 WINDOW[CURWINDOW].binary = 0;
  614.                 WINDOW[CURWINDOW].diredit = 0;
  615.                 WINDOW[CURWINDOW].news = 3;
  616.                 if(keybuf == 'p')
  617.                     WINDOW[CURWINDOW].news = 4;
  618.                 else if(keybuf == 'r' || keybuf == 'R')
  619.                     set_window(1);    /* move back to the article list window */
  620.             }
  621.             else    /* the article window already appears */
  622.             {
  623.                 if(WINDOW[n].filename)
  624.                     ifree(WINDOW[n].filename);
  625.                 WINDOW[n].filename = (Char *)imalloc(strlen(artcom) + 1);
  626.                 strcpy(WINDOW[n].filename,artcom);
  627.                 toss_data(WINDOW[n].base);
  628.                 WINDOW[n].base = base;
  629.                 WINDOW[n].currec = WINDOW[n].toprec = base->next;
  630.                 new_botrec(n);
  631.                 WINDOW[n].curbyt = 0;
  632.                 WINDOW[n].currow = WINDOW[n].toprow;
  633.                 WINDOW[n].curcol = WINDOW[n].firstcol = WINDOW[n].wantcol = 1;
  634.                 WINDOW[n].modified = WINDOW[n].diredit = WINDOW[n].binary = 0;
  635.                 WINDOW[n].news = 3;
  636.                 ref_window(n);
  637.                 if(keybuf == 'v' || keybuf == 'V')
  638.                     set_window(n);
  639.                 if(keybuf == 'p')
  640.                     WINDOW[n].news = 4;
  641.                 save_window();
  642.             }
  643.         }
  644.     }
  645.     else
  646. #endif
  647.     if(WINDOW[CURWINDOW].diredit)
  648.     {
  649.         if(CURREC == BASE)
  650.         {
  651.             abort_key();
  652.             bell();
  653.             break;
  654.         }
  655.         else
  656.         {
  657.             unselect();
  658.             curdirpos = dir_depth(WINDOW[CURWINDOW].filename);
  659. /* generate the complete selected file name */
  660.             strcpy(buf,WINDOW[CURWINDOW].filename);
  661.             if((p = strstr(buf," - GREP window")))
  662.                 *p = '\0';
  663.             else if((p = strstr(buf," - PERG window")))
  664.                 *p = '\0';
  665.             strcat(buf,CURREC->data + filename_offset());
  666. /* handle symbolic link format for ftp diredits */
  667.             if((p = strstr(buf," -> ")))
  668.                 *p = '\0';
  669. /* store the position in the current listing, unless they're going up */
  670.             if(strcmp(CURREC->data + filename_offset(),"[-]") && strcmp(CURREC->data + filename_offset(),".."))
  671.             {
  672.                 get_position(&record,&byte);
  673.                 dirpos[curdirpos] = record;
  674.                 upmove = 0;
  675.             }
  676.             else
  677.                 upmove = 1;
  678. /* parse VMS-y dirnames */
  679.             if(!ftp_unixy(ftp_system(buf)))
  680.             {
  681.                 while((p = strstr(buf,"][")))
  682.                     for(q = p + 2;(*p++ = *q++););
  683.                 if((p = strstr(buf,"-]")))
  684.                 {
  685.                     while(*--p != '.')
  686.                         if(*p == '[')
  687.                             break;
  688.                     if(*p == '[')
  689.                         strcpy(++p,"000000]");
  690.                     else
  691.                         strcpy(p,"]");
  692.                 }
  693. /* this code used to turn [000000.X] to [X]. some servers are picky.
  694.                 if((p = strstr(buf,"[000000.")))
  695.                 {
  696.                     p++;
  697.                     for(q = p + 7;(*p++ = *q++););
  698.                 }*/
  699.             }
  700. /* if they selected a directory */
  701.             if(CURREC->data[0] == 'd' || CURREC->data[0] == 'l')
  702.             {
  703.                 if(ftp_unixy(ftp_system(buf)))
  704.                     strcat(buf,"/");
  705. #ifndef NO_FTP
  706.                 if((i = host_in_name(buf)))    /* this is true if /xxx.yyy.zzz: is in the file name */
  707.                 {
  708.                     p = host_filename(i,buf,ftpfile);    /* this does all the parsing */
  709.                     if((base = dir_find(CURWINDOW,ftpfile)))    /* check whether we've visited here yet */
  710.                     {
  711.                         strcpy(fname,ftpfile);
  712.                         goto dispwin;
  713.                     }
  714.                 }
  715. #endif
  716. /* create new buffer */
  717.                 base = (rec_ptr)imalloc(sizeof(rec_node));
  718.                 base->length = 0;
  719.                 base->data = NULL;
  720.                 base->prev = base->next = base;
  721. /* check for return to a GREP window */
  722.                 curdirpos = dir_depth(fname);
  723.                 if(upmove && grepdata[curdirpos - 1])
  724.                 {
  725.                     copy_records(grepdata[curdirpos - 1],base,0,0);
  726.                     strcpy(fname,greptitle[curdirpos - 1]);
  727.                     goto dispwin;
  728.                 }
  729. /* read in the directory */
  730.                 if(read_in_diredit(fname,buf,base) > 0)
  731.                 {
  732. dispwin:
  733.                     if(host_in_name(ftpfile))
  734.                         dir_store(CURWINDOW,ftpfile,base);
  735.                     else
  736.                         toss_data(BASE);
  737.                     BASE = base;
  738.                     CURROW = TOPROW;
  739.                     CURCOL = 1;
  740.                     FIRSTCOL = 1;
  741.                     WANTCOL = 1;
  742.                     DIRECTION = 1;
  743.                     SELREC = 0;
  744.                     TOPREC = BOTREC = CURREC = BASE->next;
  745. /* do magical parsing ad nauseam */
  746.                     if(upmove)
  747.                     {
  748.                         curdirpos = dir_depth(fname);
  749.                         if(dirpos[curdirpos])
  750.                             position_buffer(dirpos[curdirpos],0);
  751.                         else    /* we haven't been here yet, find olddir in the list */
  752.                         {
  753.                             strcpy(buf,WINDOW[CURWINDOW].filename);
  754.                             if(ftp_unixy(ftp_system(buf)))
  755.                             {
  756.                                 buf[strlen(buf) - 1] = '\0';    /* strip the final '/' */
  757.                                 if(strlen(buf))
  758.                                 {
  759.                                     p = strrchr(buf,'/') + 1;    /* point to dirname */
  760.                                     for(i = 1,rec = BASE->next;rec != BASE;rec = rec->next,i++)
  761.                                         if(!strcmp(rec->data + filename_offset(),p))
  762.                                             break;
  763.                                     position_buffer(i,0);
  764.                                 }
  765.                                 else
  766.                                     position_buffer(1,0);
  767.                             }
  768.                             else
  769.                             {
  770.                                 for(p = buf + strlen(buf);p != buf;)
  771.                                 {
  772.                                     if(*--p == ':')
  773.                                         break;
  774.                                     if(*p == '[')
  775.                                         break;
  776.                                     if(*p == '.')
  777.                                         break;
  778.                                 }
  779.                                 if(*p == ':')
  780.                                     position_buffer((dirpos[curdirpos] = 1),0);
  781.                                 else
  782.                                 {
  783.                                     if(*p == '.')
  784.                                         *--p = '[';
  785.                                     else
  786.                                     {
  787.                                         *p-- = '.';
  788.                                         *p = '[';
  789.                                     }
  790.                                     for(i = 1,rec = BASE->next;rec != BASE;rec = rec->next,i++)
  791.                                         if(!inscmp(rec->data + filename_offset(),p))
  792.                                             break;
  793.                                     position_buffer(i,0);
  794.                                 }
  795.                             }
  796.                         }
  797.                     }
  798.                     for(i = TOPROW;i < BOTROW;i++)    /* establish BOTREC */
  799.                     {
  800.                         if(BOTREC == BASE)
  801.                             break;
  802.                         BOTREC = BOTREC->next;
  803.                     }
  804.                     if(i < BOTROW)
  805.                         BOTREC = 0;
  806.                     ifree(WINDOW[CURWINDOW].filename);
  807.                     WINDOW[CURWINDOW].filename = (Char *)imalloc(strlen(fname) + 1);    /* this line cannot be merged with the following one... */
  808.                     strcpy(WINDOW[CURWINDOW].filename,fname);    /* ...without generating a pointer mismatch error under VMS cc */
  809.                     WINDOW[CURWINDOW].base = BASE;
  810.                     save_window();
  811.                     ref_window(CURWINDOW);
  812.                 }
  813.                 else    /* read_in_diredit failed, if it's a link, try doing it more directly */
  814.                 {
  815.                     if(CURREC->data[0] == 'l')
  816.                         goto open_window;
  817.                     ifree(base);
  818.                     bell();
  819.                 }
  820.                 break;
  821.             }
  822. open_window:
  823.             wincom_special();
  824.             emulate_key((Schar)KEY_WINDOW,1);
  825.             emulate_key((Schar)'o',1);
  826.             emulate_key((Schar)KEY_CARRIAGERETURN,1);
  827.             strcpy(buf,WINDOW[CURWINDOW].filename);
  828.             if((p = strstr(buf," - GREP window")))
  829.                 *p = '\0';
  830.             else if((p = strstr(buf," - PERG window")))
  831.                 *p = '\0';
  832.             for(i = strlen((p = buf));i--;emulate_key((Schar)*p++,1));
  833.             strcpy(buf,CURREC->data + filename_offset());
  834. /* set grepdata to NULL, tossing anything that was there (a quasi-memory-leak) */
  835.             curdirpos = dir_depth(buf);
  836.             if(grepdata[curdirpos])
  837.             {
  838.                 toss_data(grepdata[curdirpos]);
  839.                 grepdata[curdirpos] = NULL;
  840.             }
  841.             if(greptitle[curdirpos])
  842.             {
  843.                 ifree(greptitle[curdirpos]);
  844.                 greptitle[curdirpos] = NULL;
  845.             }
  846. /* handle symbolic link format for ftp diredits */
  847.             if((p = strstr(buf," -> ")))
  848.                 *p = '\0';
  849.             for(i = 0;i < strlen(buf);i++)
  850.                 emulate_key((Schar)buf[i],1);
  851.             emulate_key((Schar)KEY_CARRIAGERETURN,1);
  852.             if(host_in_name(WINDOW[CURWINDOW].filename))
  853.                 ftp_fileonly();
  854.         }
  855.     }
  856.     else
  857.     {
  858.         carriage_return(repeat);
  859.         WINDOW[CURWINDOW].modified = 1;
  860.     }
  861.     break;
  862.  
  863. case KEY_DELTOBEGLINE:             /* delete to beginning of line */
  864.     emulate_key((Schar)KEY_DELTOBEGLINE2,repeat);
  865.     WINDOW[CURWINDOW].modified = 1;
  866.     break;
  867.  
  868. case KEY_REFRESH:             /* refresh screen */
  869.     ref_display();
  870.     break;
  871.  
  872. case KEY_DELTOBEGLINE2:             /* delete to beginning of line */
  873.     if(!find_line(-repeat))
  874.     {
  875.         abort_key();
  876.         break;
  877.     }
  878.     if(OVERSTRIKE)
  879.     {
  880.         offset = find_line(-repeat);
  881.         killer(offset,&LINEBUF,0);
  882.     }
  883.     else
  884.     {
  885.         if(repeat > 1)
  886.         {
  887.             offset = find_line(-(repeat - 1));
  888.             killer(offset,0,0);
  889.             LINEBUF.direction = 1;
  890.         }
  891.         offset = find_line(-1);
  892.         killer(offset,&LINEBUF,0);
  893.     }
  894.     LINEBUF.direction = 1;
  895.     WINDOW[CURWINDOW].modified = 1;
  896.     break;
  897.  
  898. /*case KEY_CTRLY:*/             /* control-y */
  899. /*    emulate_key((Schar)KEY_REFRESH,repeat);
  900.     break;*/
  901.  
  902. case KEY_COMMAND:             /* command */
  903.     emulate_key((Schar)KEY_COMMAND2,repeat);
  904.     break;
  905.  
  906. case KEY_DELETE:             /* delete */
  907.     if(!find_char(-repeat))
  908.     {
  909.         abort_key();
  910.         break;
  911.     }
  912.     if(OVERSTRIKE)
  913.         killer(-repeat,&CHARBUF,0);
  914.     else
  915.     {
  916.         if(repeat > 1)
  917.             killer(1L - repeat,0,0);
  918.         killer(-1L,&CHARBUF,0);
  919.     }
  920.     CHARBUF.direction = 1;
  921.     WINDOW[CURWINDOW].modified = 1;
  922.     break;
  923.  
  924. /*******************************************************************************/
  925. /*                                 keypad keys                                 */
  926. /*******************************************************************************/
  927.  
  928. case KEY_UPARROW:             /* up arrow */
  929.     copy_only(0);
  930.     up_arrow(repeat);
  931.     break;
  932.  
  933. case KEY_DOWNARROW:             /* down arrow */
  934.     copy_only(0);
  935.     down_arrow(repeat);
  936.     break;
  937.  
  938. case KEY_RIGHTARROW:             /* right arrow */
  939.     copy_only(0);
  940.     right_arrow(repeat);
  941.     break;
  942.  
  943. case KEY_LEFTARROW:             /* left arrow */
  944.     copy_only(0);
  945.     left_arrow(repeat);
  946.     break;
  947.  
  948. case KEY_MOVEBYLINE:             /* move by line */
  949.     if(WINDOW[CURWINDOW].diredit && DIRECTION == -1)
  950.         emulate_key((Schar)KEY_UPARROW,repeat);
  951.     else
  952.     {
  953.         copy_only(0);
  954.         move_line(repeat);
  955.     }
  956.     break;
  957.  
  958. case KEY_MOVEBYWORD:             /* move by word */
  959.     copy_only(0);
  960.     move_word(repeat);
  961.     break;
  962.  
  963. case KEY_MOVEBYEOL:             /* move to end of line */
  964.     copy_only(0);
  965.     move_eol(repeat);
  966.     break;
  967.  
  968. case KEY_MOVEBYCHAR:             /* move by character */
  969.     copy_only(0);
  970.     if(DIRECTION == 1)
  971.         right_arrow(repeat);
  972.     else
  973.         left_arrow(repeat);
  974.     break;
  975.  
  976. case KEY_FORWARD:             /* set forward */
  977.     copy_only(0);
  978.     DIRECTION = 1;
  979.     break;
  980.  
  981. case KEY_BACKWARD:             /* set backward */
  982.     copy_only(0);
  983.     DIRECTION = -1;
  984.     break;
  985.  
  986. case KEY_KILL:             /* kill */
  987.     match_init(SEARCH_FLAGS);
  988.     if(SELREC != 0)
  989.     {
  990.         offset = get_offset(SELREC,SELBYT,get_seldir());
  991.         if(WINDOW[CURWINDOW].diredit || WINDOW[CURWINDOW].news == 1 || WINDOW[CURWINDOW].news == 2)
  992.         {
  993.             if(offset < 0 && SELREC != CURREC)
  994.             {
  995.                 rec1 = SELREC;
  996.                 rec2 = CURREC->prev;
  997.             }
  998.             else if(offset > 0 && SELREC != CURREC)
  999.             {
  1000.                 rec1 = CURREC;
  1001.                 rec2 = SELREC->prev;
  1002.             }
  1003.             else
  1004.                 break;
  1005.             while(1)
  1006.             {
  1007. #ifndef NO_NEWS
  1008.                 if(WINDOW[CURWINDOW].news == 2)    /* handle newsrc dbs if it's an article window */
  1009.                 {
  1010.                     my_sscanf(rec1->data,"%d",&artnum);
  1011.                     if(rec1->recflags & 2)    /* the article is marked read */
  1012.                         newsrc_mark_unread(artnum);
  1013.                     else
  1014.                         newsrc_mark_read(artnum);
  1015.                 }
  1016. #endif
  1017.                 rec1->recflags ^= 2;
  1018.                 if(rec1 == rec2)
  1019.                     break;
  1020.                 rec1 = rec1->next;
  1021.             }
  1022.             unselect();
  1023.             ref_window(CURWINDOW);
  1024.         }
  1025.         else
  1026.         {
  1027.             unselect();
  1028.             if(BOXCUT)
  1029.             {
  1030.                 if(WINDOW[CURWINDOW].binary)
  1031.                 {
  1032.                     slip_message("You can't use box mode in binary buffers.");
  1033.                     wait_message();
  1034.                     abort_key();
  1035.                     break;
  1036.                 }
  1037.                 else
  1038.                     killer_box();
  1039.             }
  1040.             killer(offset,&KILLBUF,0);
  1041.             if(offset < 0)
  1042.                 KILLBUF.direction = 1;
  1043.             else
  1044.                 KILLBUF.direction = -1;
  1045.         }
  1046.     }
  1047.     else if((offset = match_search(CURREC,CURBYT,&SEARCHBUF)) != 0)
  1048.     {
  1049.         killer(offset,&KILLBUF,0);
  1050.         KILLBUF.direction = 1;
  1051.     }
  1052.     WINDOW[CURWINDOW].modified = 1;
  1053.     break;
  1054.  
  1055. case KEY_MOVEBYPAGE:             /* move by page */
  1056.     copy_only(0);
  1057.     if(!PAGE_BREAK_LENGTH)
  1058.         break;
  1059.     load_buffer(&pagebuf,PAGE_BREAK,PAGE_BREAK_LENGTH);
  1060.     match_init("ebnnn");
  1061.     if(DIRECTION < 0 && !match_search(CURREC,CURBYT,&pagebuf))
  1062.         repeat++;
  1063.     offset = find_string(CURREC,CURBYT,DIRECTION,&pagebuf,"ebnnn",repeat);
  1064.     if(DIRECTION == 1)
  1065.     {
  1066.         if(offset != 0)
  1067.             right_arrow(offset);
  1068.         else
  1069.         {
  1070.             abort_key();
  1071.             emulate_key((Schar)KEY_ENDOFFILE,1);    /* treat as move-to-end-of-file */
  1072.         }
  1073.     }
  1074.     else
  1075.     {
  1076.         if(offset != 0)
  1077.             left_arrow(-offset);
  1078.         else
  1079.         {
  1080.             abort_key();
  1081.             emulate_key((Schar)KEY_TOPOFFILE,1);    /* treat as move-to-top-of-file */
  1082.         }
  1083.     }
  1084.     if(offset != 0 && CURROW <= BOTLIM)
  1085.     {
  1086.         down_arrow(BOTLIM - TOPROW);
  1087.         up_arrow(BOTLIM - TOPLIM);
  1088.     }
  1089.     break;
  1090.  
  1091. case KEY_MOVEBYSECTION:             /* move by section */
  1092.     copy_only(0);
  1093.     if(SECTION_LINES <= 0)
  1094.     {
  1095.         if((i = repeat * ((BOTROW - TOPROW) + SECTION_LINES)) < 1)
  1096.             i = 1;
  1097.     }
  1098.     else
  1099.         i = repeat * SECTION_LINES;
  1100.     if(DIRECTION == 1)
  1101.         down_arrow(i);
  1102.     else
  1103.         up_arrow(i);
  1104.     break;
  1105.  
  1106. case KEY_APPEND:             /* append */
  1107.     match_init(SEARCH_FLAGS);
  1108.     if(SELREC != 0)
  1109.     {
  1110.         offset = get_offset(SELREC,SELBYT,get_seldir());
  1111.         unselect();
  1112.         if(BOXCUT)
  1113.             killer_box();
  1114.         killer(offset,&KILLBUF,1);
  1115.     }
  1116.     else if((offset = match_search(CURREC,CURBYT,&SEARCHBUF)) != 0)
  1117.         killer(offset,&KILLBUF,1);
  1118.     KILLBUF.direction = 1;
  1119.     WINDOW[CURWINDOW].modified = 1;
  1120.     break;
  1121.  
  1122. case KEY_DELWORD:             /* delete word */
  1123.     if(!find_word(repeat))
  1124.     {
  1125.         abort_key();
  1126.         copy_only(0);
  1127.         break;
  1128.     }
  1129.     if(OVERSTRIKE)
  1130.     {
  1131.         offset = find_word(repeat);
  1132.         killer(offset,&WORDBUF,0);
  1133.     }
  1134.     else
  1135.     {
  1136.         if(repeat > 1)
  1137.         {
  1138.             offset = find_word(repeat - 1);
  1139.             killer(offset,0,0);
  1140.         }
  1141.         offset = find_word(1);
  1142.         killer(offset,&WORDBUF,0);
  1143.     }
  1144.     WORDBUF.direction = -1;
  1145.     WINDOW[CURWINDOW].modified = 1;
  1146.     break;
  1147.  
  1148. case KEY_DELCHAR:             /* delete character */
  1149.     if(!find_char(repeat))
  1150.     {
  1151.         abort_key();
  1152.         copy_only(0);
  1153.         break;
  1154.     }
  1155.     if(OVERSTRIKE)
  1156.         killer(repeat,&CHARBUF,0);
  1157.     else
  1158.     {
  1159.         if(repeat > 1)
  1160.             killer(repeat - 1L,0,0);
  1161.         killer(1L,&CHARBUF,0);
  1162.     }
  1163.     CHARBUF.direction = -1;
  1164.     WINDOW[CURWINDOW].modified = 1;
  1165.     break;
  1166.  
  1167. case KEY_SELECT:             /* select */
  1168.     copy_only(0);
  1169.     unselect();    /* remove selection if it is already active */
  1170.     SELREC = CURREC;
  1171.     SELBYT = CURBYT;
  1172.     SELDIR = 0;
  1173.     move(CURROW,CURCOL);
  1174.     doselect(CURREC,CURBYT);
  1175.     move(CURROW,CURCOL);
  1176.     break;
  1177.  
  1178. case KEY_ENTER:             /* enter */
  1179.     copy_only(0);
  1180.     break;
  1181.  
  1182. case KEY_GOLD:             /* gold */
  1183.     copy_only(0);
  1184.     reverse();
  1185.     putz("That was interesting.");
  1186.     normal();
  1187.     break;
  1188.  
  1189. case KEY_SETCOPY:             /* set copy select */
  1190.     copy_only(1);
  1191.     break;
  1192.  
  1193. case KEY_FINDNEXT:             /* find next */
  1194.     copy_only(0);
  1195.     if(WINDOW[CURWINDOW].diredit && DIRECTION == -1)
  1196.     {
  1197.         i = CURBYT;
  1198.         CURBYT = 0;
  1199.     }
  1200.     if((offset = find_string(CURREC,CURBYT,DIRECTION,&SEARCHBUF,SEARCH_FLAGS,repeat)) != 0)
  1201.     {
  1202.         if(DIRECTION == 1)
  1203.             right_arrow(offset);
  1204.         else
  1205.             left_arrow(-offset);
  1206.     }
  1207.     else
  1208.     {
  1209.         if(WINDOW[CURWINDOW].diredit && DIRECTION == -1)
  1210.             CURBYT = i;
  1211.         abort_key();
  1212.         bell();
  1213.     }
  1214.     break;
  1215.  
  1216. case KEY_DELLINE:             /* delete line */
  1217.     if(WINDOW[CURWINDOW].diredit || WINDOW[CURWINDOW].news == 1 || WINDOW[CURWINDOW].news == 2)
  1218.     {
  1219. #ifndef NO_NEWS
  1220.         if(WINDOW[CURWINDOW].news == 2)
  1221.         {
  1222.             my_sscanf(CURREC->data,"%d",&artnum);
  1223.             if(CURREC->recflags & 2)    /* the article is marked read */
  1224.                 newsrc_mark_unread(artnum);
  1225.             else
  1226.                 newsrc_mark_read(artnum);
  1227.         }
  1228. #endif
  1229.         CURREC->recflags ^= 2;
  1230.         paint(CURROW,CURROW,FIRSTCOL);
  1231.     }
  1232.     else
  1233.     {
  1234.         if(!find_line(repeat))
  1235.         {
  1236.             abort_key();
  1237.             copy_only(0);
  1238.             break;
  1239.         }
  1240.         if(OVERSTRIKE)
  1241.         {
  1242.             offset = find_line(repeat);
  1243.             killer(offset,&LINEBUF,0);
  1244.         }
  1245.         else
  1246.         {
  1247.             if(repeat > 1)
  1248.             {
  1249.                 offset = find_line(repeat - 1);
  1250.                 killer(offset,0,0);
  1251.             }
  1252.             offset = find_line(1);
  1253.             killer(offset,&LINEBUF,0);
  1254.         }
  1255.         LINEBUF.direction = -1;
  1256.         WINDOW[CURWINDOW].modified = 1;
  1257.     }
  1258.     break;
  1259.  
  1260. case KEY_OPENLINE:             /* open line */
  1261.     copy_only(0);
  1262.     openline(repeat);
  1263.     WINDOW[CURWINDOW].modified = 1;
  1264.     break;
  1265.  
  1266. case KEY_CHANGECASE:             /* change case */
  1267.     switch(CASECHANGE)
  1268.     {
  1269.         case CASE_UPPER:
  1270.             casetable = _my_toupper_;
  1271.             break;
  1272.         case CASE_LOWER:
  1273.             casetable = _my_tolower_;
  1274.             break;
  1275.         case CASE_OPPOSITE:
  1276.             casetable = _my_tooppos_;
  1277.             break;
  1278.         case CASE_CAPITALIZE:
  1279.             casetable = NULL;
  1280.             break;
  1281.     }
  1282.     copy_only(0);
  1283.     match_init(SEARCH_FLAGS);
  1284.     if(SELREC != 0)
  1285.     {
  1286.         offset = get_offset(SELREC,SELBYT,get_seldir());
  1287.         unselect();
  1288.         change_case(offset,0,casetable);
  1289.     }
  1290.     else if((offset = match_search(CURREC,CURBYT,&SEARCHBUF)) != 0)
  1291.         change_case(offset,(SEARCH_FLAGS[((int)SMODE_EXACT) >> 1] == 'e')? 0 : 1,casetable);
  1292.     else
  1293.         if(!change_case(repeat * DIRECTION,1,casetable))
  1294.             abort_key();
  1295.     WINDOW[CURWINDOW].modified = 1;
  1296.     break;
  1297.  
  1298. case KEY_DELTOEOL:             /* delete to end of line */
  1299.     if(!find_line(repeat))
  1300.     {
  1301.         abort_key();
  1302.         copy_only(0);
  1303.         break;
  1304.     }
  1305.     if(OVERSTRIKE)
  1306.     {
  1307.         offset = find_line(repeat) - 1;
  1308.         killer(offset,&LINEBUF,0);
  1309.     }
  1310.     else
  1311.     {
  1312.         if(repeat > 1)
  1313.         {
  1314.             offset = find_line(repeat - 1) - 1;
  1315.             killer(offset,0,0);
  1316.         }
  1317.         offset = find_line(1) - 1;
  1318.         killer(offset,&LINEBUF,0);
  1319.     }
  1320.     LINEBUF.direction = -1;
  1321.     WINDOW[CURWINDOW].modified = 1;
  1322.     break;
  1323.  
  1324. case KEY_SPECIALINSERT:             /* special insert */
  1325.     copy_only(0);
  1326.     keybuf = (repeat < 128)? repeat : (repeat - 256);
  1327.     repeat = 1;
  1328.     goto insert_char;
  1329.  
  1330. case KEY_ENDOFFILE:             /* bottom of file */
  1331.     copy_only(0);
  1332.     forgive();
  1333.     down_arrow(0x7fffffff);
  1334.     begrudge();
  1335.     WANTCOL = 0;
  1336.     break;
  1337.  
  1338. case KEY_TOPOFFILE:             /* top of file */
  1339.     copy_only(0);
  1340.     forgive();
  1341.     up_arrow(0x7fffffff);
  1342.     left_arrow(0x7fffffff);
  1343.     begrudge();
  1344.     WANTCOL = 0;
  1345.     break;
  1346.  
  1347. case KEY_UNKILL:             /* unkill */
  1348.     copy_only(0);
  1349.     for(i = 0;i < repeat;i++)
  1350.     {
  1351.         if(BOXCUT)
  1352.             insert_box();
  1353.         insert(&KILLBUF);
  1354.     }
  1355.     WINDOW[CURWINDOW].modified = 1;
  1356.     break;
  1357.  
  1358. case KEY_COMMAND2:             /* command */
  1359.     copy_only(0);
  1360.     if((i = command(grepdata,greptitle)))
  1361.     {
  1362.         if(i == 1)    /* normal EXIT, QUIT etc. */
  1363.         {
  1364.             for(i = 0;i < sizeof(grepdata) / sizeof(rec_ptr);i++)
  1365.             {
  1366.                 if(grepdata[i])
  1367.                 {
  1368.                     toss_data(grepdata[i]);
  1369.                     grepdata[i] = NULL;
  1370.                 }
  1371.                 if(greptitle[i])
  1372.                 {
  1373.                     ifree(greptitle[i]);
  1374.                     greptitle[i] = NULL;
  1375.                 }
  1376.             }
  1377.             return;
  1378.         }
  1379. #ifndef NO_NEWS
  1380. /* find the posting window */
  1381.         for(close = 0;close < NWINDOWS;close++)
  1382.             if(WINDOW[close].news == 4)
  1383.                 break;
  1384. /* insert the Lines header */
  1385. rescan:
  1386.         if(WINDOW[close].base->next == WINDOW[close].base)
  1387.         {
  1388. bad_posting:
  1389.             slip_message("That posting doesn't seem to contain anything, ignoring it.");
  1390.             wait_message();
  1391.             goto clean_posting;
  1392.         }
  1393.         if(i == -2)
  1394.         {
  1395.             slip_message("Posting aborted.");
  1396.             wait_message();
  1397.             goto clean_posting;
  1398.         }
  1399.         for(j = k = 0,rec = WINDOW[close].base->next;rec != WINDOW[close].base;rec = rec->next,j++)
  1400.             if(!k && !rec->length)    /* found a blank line */
  1401.                 if(!j)    /* it is at top of file, remove it and try again */
  1402.                 {
  1403.                     ifree(rec->data);
  1404.                     remq(rec);
  1405.                     ifree(rec);
  1406.                     goto rescan;
  1407.                 }
  1408.                 else
  1409.                 {
  1410.                     save_rec = rec;    /* save blank line position, Lines: will go preceeding it */
  1411.                     k = j;
  1412.                 }
  1413.         if(!(bodycount = j - k - 1))    /* total lines in body. if zero, find the Reply-To: line and insert count following that */
  1414.         {
  1415.             for(j = k = 0,rec = WINDOW[close].base->next;rec != WINDOW[close].base;rec = rec->next,j++)
  1416.                 if(!k && !strncmp(rec->data,"Reply-To: ",strlen("Reply-To: ")))
  1417.                 {
  1418.                     save_rec = rec;
  1419.                     k = j;
  1420.                     break;
  1421.                 }
  1422.             if(!(bodycount = j - k - 1))
  1423.                 goto bad_posting;
  1424.             new = (rec_ptr)imalloc(sizeof(rec_node));
  1425.             *(new->data = (Char *)imalloc(1)) = '\0';
  1426.             new->length = 0;
  1427.             new->recflags = 1;
  1428.             insq(new,save_rec);    /* insert a blank record following Reply-To: */
  1429.             save_rec = save_rec->next;
  1430.         }
  1431. /* insert the Lines: header */
  1432.         new = (rec_ptr)imalloc(sizeof(rec_node));
  1433.         sprintf(buf,"Lines: %d",bodycount);
  1434.         new->data = (Char *)imalloc((new->length = strlen(buf)) + 1);
  1435.         strcpy(new->data,buf);
  1436.         new->recflags = 1;
  1437.         insq(new,save_rec->prev);
  1438. /* post the article */
  1439.         news_command("POST");
  1440.         news_response(buf,sizeof(buf));
  1441.         if(strncmp(buf,"340 ",4))
  1442.         {
  1443.             bell();
  1444.             slip_message(buf + 4);
  1445.             wait_message();
  1446.             goto clean_posting;
  1447.         }
  1448.         for(rec = WINDOW[close].base->next;rec != WINDOW[close].base;rec = rec->next)
  1449.             news_command(rec->data);
  1450.         news_command(".");
  1451.         news_response(buf,sizeof(buf));
  1452.         slip_message(buf + 4);
  1453.         wait_message();
  1454. clean_posting:
  1455.         remove_window(close);
  1456.         for(i = 0;i < NMARK;i++)
  1457.         {
  1458.             if(MARKWINDOW[i] == close)
  1459.             {
  1460.                 for(j = i + 1;j < NMARK;j++)
  1461.                 {
  1462.                     MARKWINDOW[j - 1] = MARKWINDOW[j];
  1463.                     MARKREC[j - 1] = MARKREC[j];
  1464.                     MARKBYT[j - 1] = MARKBYT[j];
  1465.                 }
  1466.                 NMARK--;
  1467.                 i--;
  1468.             }
  1469.         }
  1470. #endif    /* NO_NEWS */
  1471.     }
  1472.     break;
  1473.  
  1474. case KEY_FILL:             /* fill */
  1475.     copy_only(0);
  1476.     if(!SELREC)
  1477.     {
  1478.         abort_key();
  1479.         break;
  1480.     }
  1481.     word_fill();
  1482.     WINDOW[CURWINDOW].modified = 1;
  1483.     break;
  1484.  
  1485. case KEY_REPLACE:             /* replace */
  1486.     copy_only(0);
  1487.     match_init(SEARCH_FLAGS);
  1488.     if((offset = match_search(CURREC,CURBYT,&SEARCHBUF)) != 0)
  1489.     {
  1490.         killer(offset,&pagebuf,0);
  1491.         insert(&KILLBUF);
  1492.         WINDOW[CURWINDOW].modified = 1;
  1493.     }
  1494.     else
  1495.         abort_key();
  1496.     break;
  1497.  
  1498. case KEY_UNDELWORD:             /* undelete word */
  1499.     copy_only(0);
  1500.     for(i = 0;i < repeat;i++)
  1501.         insert(&WORDBUF);
  1502.     WINDOW[CURWINDOW].modified = 1;
  1503.     break;
  1504.  
  1505. case KEY_UNDELCHAR:             /* undelete character */
  1506.     copy_only(0);
  1507.     for(i = 0;i < repeat;i++)
  1508.         insert(&CHARBUF);
  1509.     WINDOW[CURWINDOW].modified = 1;
  1510.     break;
  1511.  
  1512. case KEY_UNSELECT:             /* unselect */
  1513.     copy_only(0);
  1514.     unselect();
  1515.     break;
  1516.  
  1517. case KEY_SUBSTITUTE:             /* substitute */
  1518.     copy_only(0);
  1519.     match_init(SEARCH_FLAGS);
  1520.     if((searchlength = match_search(CURREC,CURBYT,&SEARCHBUF)) != 0)
  1521.     {
  1522.         KILLBUF.direction = DIRECTION;
  1523.         if(repeat > 1)
  1524.         {
  1525.             was_displaying = puttest();
  1526.             putoff();
  1527.         }
  1528.         else
  1529.             toggle_paren();
  1530.         for(i = 0;i < repeat;i++)
  1531.         {
  1532.             killer(searchlength,&pagebuf,0);
  1533.             WINDOW[CURWINDOW].modified = 1;
  1534.             if(KILLBUF.nrecs > 0)
  1535.                 insert(&KILLBUF);
  1536.             if(DIRECTION == 1)
  1537.                 left_arrow(1);    /* otherwise, we might already be sitting on the next string to replace */
  1538.             if((offset = find_string(CURREC,CURBYT,DIRECTION,&SEARCHBUF,SEARCH_FLAGS,1)) != 0)
  1539.             {
  1540.                 if(DIRECTION == 1)
  1541.                     right_arrow(offset);
  1542.                 else
  1543.                     left_arrow(-offset);
  1544.             }
  1545.             else
  1546.             {
  1547.                 bell();
  1548.                 emulate_abort();
  1549.                 abort_key();
  1550.                 break;
  1551.             }
  1552.         }
  1553.         KILLBUF.direction = 1;
  1554.         if(repeat <= 1)        /* CWS 11-93 non-display toggle bug */
  1555.             toggle_paren();
  1556.         else if(was_displaying)
  1557.         {
  1558.             puton();
  1559.             ref_display();
  1560.             move(CURROW,CURCOL);
  1561.         }
  1562.     }
  1563.     else
  1564.         abort_key();
  1565.     break;
  1566.  
  1567. case KEY_GOLDGOLD:             /* gold-gold */
  1568.     copy_only(0);
  1569.     break;
  1570.  
  1571. case KEY_WINDOW:             /* window */
  1572.     copy_only(0);
  1573.     wincom();
  1574.     break;
  1575.  
  1576. case KEY_FIND:             /* find */
  1577.     copy_only(0);
  1578.     i = inquire("Search for",buf,sizeof(buf),0);
  1579.     paint(BOTROW,BOTROW,FIRSTCOL);
  1580.     if(!i)
  1581.         break;
  1582.     term = get_terminator();
  1583.     if(term == KEY_FORWARD)
  1584.         DIRECTION = 1;
  1585.     else if(term == KEY_BACKWARD)
  1586.         DIRECTION = -1;
  1587.     load_buffer(&SEARCHBUF,buf,i);
  1588.     if(SEARCH_FLAGS[((int)SMODE_CHAR) >> 1] == 'w' && SEARCHBUF.nrecs > 1)
  1589.     {
  1590.         buffer_empty(&SEARCHBUF);
  1591.         slip_message("Embedded <cr>s don't work when SET SEARCH WORD is in effect.");
  1592.         wait_message();
  1593.         break;
  1594.     }
  1595.     if((offset = find_string(CURREC,CURBYT,DIRECTION,&SEARCHBUF,SEARCH_FLAGS,repeat)) != 0)
  1596.     {
  1597.         if(DIRECTION == 1)
  1598.             right_arrow(offset);
  1599.         else
  1600.             left_arrow(-offset);
  1601.     }
  1602.     else
  1603.     {
  1604.         emulate_abort();
  1605.         abort_key();
  1606.         bell();
  1607.     }
  1608.     break;
  1609.  
  1610. case KEY_UNDELLINE:             /* undelete line */
  1611.     copy_only(0);
  1612.     for(i = 0;i < repeat;i++)
  1613.         insert(&LINEBUF);
  1614.     WINDOW[CURWINDOW].modified = 1;
  1615.     break;
  1616.  
  1617. case KEY_RETURN:             /* gold up (return to spot) */
  1618.     copy_only(0);
  1619.     for(i = NMARK - 1;i >= 0;i--)
  1620.         if(MARKWINDOW[i] != CURWINDOW || MARKREC[i] != CURREC || MARKBYT[i] != CURBYT)
  1621.         {
  1622.             save_window();
  1623.             set_window(MARKWINDOW[i]);
  1624.             offset = get_offset(MARKREC[i],MARKBYT[i],get_markdir(MARKREC[i],MARKBYT[i]));
  1625.             if(offset < 0)
  1626.                 left_arrow(-offset);
  1627.             else if(j > 0)
  1628.                 right_arrow(offset);
  1629.             break;
  1630.         }
  1631.     break;
  1632.  
  1633. case KEY_MARK:             /* gold down (mark spot) */
  1634.     copy_only(0);
  1635.     for(i = 0;i < NMARK;i++)
  1636.         if(MARKWINDOW[i] == CURWINDOW && MARKREC[i] == CURREC && MARKBYT[i] == CURBYT)
  1637.         {
  1638.             for(j = i + 1;j < NMARK;j++)
  1639.             {
  1640.                 MARKWINDOW[j - 1] = MARKWINDOW[j];
  1641.                 MARKREC[j - 1] = MARKREC[j];
  1642.                 MARKBYT[j - 1] = MARKBYT[j];
  1643.             }
  1644.             NMARK--;
  1645.             goto label41;
  1646.         }
  1647.     if(NMARK == MAX_MARKS)    /* mark a new spot */
  1648.         break;
  1649.     MARKWINDOW[NMARK] = CURWINDOW;
  1650.     MARKREC[NMARK] = CURREC;
  1651.     MARKBYT[NMARK++] = CURBYT;
  1652. label41: 
  1653.     break;
  1654.  
  1655. case KEY_SCROLLRIGHT:             /* gold right (scroll right) */
  1656.     copy_only(0);
  1657.     if(FIRSTCOL > 1)
  1658.     {
  1659.         FIRSTCOL = max(1,FIRSTCOL - NCOL / 4);
  1660.         ref_window(CURWINDOW);
  1661.     }
  1662.     deliberate = 1;
  1663.     break;
  1664.  
  1665. case KEY_SCROLLLEFT:             /* gold left (scroll left) */
  1666.     copy_only(0);
  1667.     FIRSTCOL += NCOL / 4;
  1668.     ref_window(CURWINDOW);
  1669.     deliberate = 1;
  1670.     break;
  1671.  
  1672. default:
  1673. insert_char: /* user inserts a character */
  1674.     if(WINDOW[CURWINDOW].news == 1 || WINDOW[CURWINDOW].news == 2)
  1675.         goto selective;
  1676.     if(WINDOW[CURWINDOW].diredit)
  1677.     {
  1678.         if(CURREC->data[0] == 'd')
  1679.             goto selective;
  1680.         switch(keybuf)
  1681.         {
  1682.             case 'A':
  1683.             case 'a':
  1684.             case ' ':
  1685.                 goto selective;
  1686.             case 'B':
  1687.             case 'b':
  1688.                 force_binary();
  1689.                 goto selective;
  1690.         }
  1691.     }
  1692.     copy_only(0);
  1693.     if(AUTOWRAP && (keybuf != ' ') && (CURCOL > WRAP_MARGIN))    /* handle autowrap */
  1694.         if(autowrp(keybuf))    /* returns 1 if autowrapping was possible (and was done) */
  1695.             continue;
  1696.     if(repeat == 1)
  1697.     {
  1698.         if((character = keybuf) == '}' && CFRIENDLY)
  1699.             cfrendly(&charbuf,&multibuf,&multirec);    /* deal with closure of C-block */
  1700.         else
  1701.             insert(&charbuf);    /* just insert character */
  1702.     }
  1703.     else    /* multiple-character insertion */
  1704.     {
  1705.         multirec.data = (Char *)imalloc(repeat + 1);
  1706.         memset(multirec.data,(Char)keybuf,repeat);
  1707.         multirec.length = repeat;
  1708.         insert(&multibuf);
  1709.         ifree(multirec.data);
  1710.     }
  1711.     WINDOW[CURWINDOW].modified = 1;
  1712.     break;
  1713.     
  1714. /*******************************************************************************/
  1715.         }    /* end of switch */
  1716.     }    /* end of forever loop */
  1717. }
  1718.  
  1719.